Skip to content

silent verify script#282

Open
jasonmorais wants to merge 20 commits into
mainfrom
jason/silent-verify
Open

silent verify script#282
jasonmorais wants to merge 20 commits into
mainfrom
jason/silent-verify

Conversation

@jasonmorais

@jasonmorais jasonmorais commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Summary by Sourcery

Introduce silent verification command runners and wire them into the repo-wide verify workflow while expanding local-dev utilities and docs.

New Features:

  • Add silent command runner utilities, including reusable verification sequences and tool-specific wrappers for pnpm scripts, audits, Snyk scans, Sonar checks, and test runners in @cellix/local-dev.
  • Expose a new verify.ts script that orchestrates the full verification pipeline using the silent runner abstractions and is invoked by the root verify npm script.

Enhancements:

  • Export writeJsonFile and new silent-runner APIs from @cellix/local-dev root and files entrypoints, and register the silent-runners subpath in package exports and Vitest config aliases.
  • Refine the Agent formatter to only emit summary output when issues exist, avoiding noise on fully passing runs.
  • Update @cellix/local-dev documentation and manifest to describe silent runners, verification workflows, public subpaths, and their policy boundaries.

Build:

  • Adjust pnpm workspace dependency overrides for protobufjs, js-yaml, body-parser, and OpenTelemetry packages to newer patched versions.

Tests:

  • Add Vitest coverage for silent command runners and verification sequences, including success silence, failure replay behavior, signal handling, buffer sizing, step ordering, and sequence immutability.

Chores:

  • Hook @cellix/local-dev into the root devDependencies to support the new verify pipeline script.

@sourcery-ai

sourcery-ai Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Introduce a new silent verification runner subsystem under @cellix/local-dev, wire it into the root verify workflow, extend local-dev’s public API and documentation, and refine console noise from the serenity AgentFormatter, alongside several dependency override updates.

Sequence diagram for the new silent verify workflow

sequenceDiagram
    actor Dev
    participant verify_ts as verify_ts
    participant verificationSequence as VerificationSequence
    participant silentRunners as runSilentCommandSequence
    participant childProcess as spawnSync

    Dev->>verify_ts: runVerifyCommand()
    verify_ts->>verificationSequence: addStep(pnpmScript('format:check'))
    verify_ts->>verificationSequence: addStep(architectureTests())
    verify_ts->>verificationSequence: run()
    verificationSequence->>silentRunners: runSilentCommandSequence(options)
    loop for each step
        silentRunners->>childProcess: spawnSync(command, args, options)
        childProcess-->>silentRunners: SpawnSyncReturns
        alt status === 0
            silentRunners-->>silentRunners: suppress stdout/stderr
        else status !== 0
            silentRunners-->>silentRunners: replayFailureOutput(result, streams)
            silentRunners-->>verificationSequence: SilentCommandSequenceResult(failing step)
            verificationSequence-->>verify_ts: result
            verify_ts-->>Dev: process.exitCode = result.status
        end
    end
    silentRunners-->>verificationSequence: SilentCommandSequenceResult(success)
    verificationSequence-->>verify_ts: result
    verify_ts-->>Dev: print "verify passed" and set process.exitCode
Loading

File-Level Changes

Change Details Files
Add silent command runners, verification sequence builder, and tool-specific wrappers, and expose them from @cellix/local-dev (including tests and Vitest config).
  • Introduce runSilentCommand and runSilentCommandSequence utilities that capture stdout/stderr, normalize exit codes (including signals), and replay output only on failure.
  • Add CommandSequenceStep/VerificationSequence abstractions plus a fluent verificationSequence builder supporting immutable sequence composition and per-step output/maxBuffer overrides.
  • Provide pnpm-, audit-, test-runner-, Snyk-, and Sonar-specific wrapper helpers that encode generic CLI shapes while leaving project policy (org names, args) to consumers.
  • Export the new silent runner APIs from the root local-dev index and a new @cellix/local-dev/silent-runners subpath; update vitest config aliasing and README/manifest docs to describe the new surface.
  • Add comprehensive Vitest coverage for silent runner success/failure behavior, buffering configuration, sequence short-circuiting, and verificationSequence immutability.
packages/cellix/local-dev/src/silent-runners/index.ts
packages/cellix/local-dev/src/silent-runners/pnpm.ts
packages/cellix/local-dev/src/silent-runners/pnpm-audit.ts
packages/cellix/local-dev/src/silent-runners/knip.ts
packages/cellix/local-dev/src/silent-runners/snyk.ts
packages/cellix/local-dev/src/silent-runners/sonar.ts
packages/cellix/local-dev/src/silent-runners/test-runners.ts
packages/cellix/local-dev/src/index.ts
packages/cellix/local-dev/src/index.test.ts
packages/cellix/local-dev/vitest.config.ts
Add a Node-based verify script that composes the silent runners into a reusable repository verification workflow.
  • Create build-pipeline/scripts/verify.ts which builds a cellixVerify VerificationSequence combining format, architecture tests, coverage merge, e2e tests, Knip, pnpm audits, Snyk scans, and Sonar checks.
  • Configure Snyk wrapper steps with shared org/remote-repo arguments while keeping those project-specific details in the root script rather than the shared local-dev package.
  • Update the root package.json verify script to invoke the new Node verify.ts instead of a long pnpm command chain and add @cellix/local-dev as a devDependency.
build-pipeline/scripts/verify.ts
package.json
Extend local-dev files API and documentation/manifest to reflect new capabilities and boundaries.
  • Export writeJsonFile from the local-dev files index and root index to support JSON write helpers externally.
  • Update README.md and manifest.md to describe silent runners, tool-wrapper builders, new subpath exports, output buffering behavior, and non-goals such as scanner policy ownership.
  • Document the new public exports list (silent runners and tool wrappers) and clarify package purpose/scope around verification workflows.
packages/cellix/local-dev/src/files/index.ts
packages/cellix/local-dev/src/index.ts
packages/cellix/local-dev/README.md
packages/cellix/local-dev/manifest.md
Refine serenity AgentFormatter output to suppress noise when there are no issues.
  • Change AgentFormatter.onTestRunFinished to early-return when issueCount is zero instead of logging an empty summary block.
  • Always log an Issues: N line when there are issues, removing the All scenarios passed message for the zero-issue case.
packages/cellix/serenity-framework/src/formatters/agent-formatter.ts
Adjust local-dev packaging (exports) and workspace dependency overrides for build hygiene and compatibility.
  • Expose new ./silent-runners and ./vite subpath exports from @cellix/local-dev’s package.json exports map and update the package description to include verification helpers.
  • Update pnpm-workspace overrides to bump protobufjs, js-yaml, body-parser, and several OpenTelemetry-related packages to newer versions, plus associated transitive overrides in pnpm-lock.yaml.
packages/cellix/local-dev/package.json
pnpm-workspace.yaml
pnpm-lock.yaml
Minor repo-level and docs formatting adjustments.
  • Add @cellix/local-dev as a workspace dependency at the root for the new verify script.
  • Tidy top-level readme.md spacing around sections to keep formatting consistent.
  • Touch Snyk/Knip/config-related files (e.g., .snyk, knip.json, tsconfig.vitest.json) as needed for the new tooling, without significant behavioral changes.
package.json
readme.md
.snyk
knip.json
packages/cellix/local-dev/tsconfig.vitest.json

Possibly linked issues

  • #Silent Verify Runs - Create wrappers for scripts and enfornce native silence: PR fully implements the silent verification wrappers and root verify workflow described in the issue, plus minor extras.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@jasonmorais
jasonmorais marked this pull request as ready for review June 22, 2026 15:32
@jasonmorais
jasonmorais requested a review from a team June 22, 2026 15:32
@jasonmorais
jasonmorais requested a review from a team as a code owner June 22, 2026 15:32

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 issues, and left some high level feedback:

  • In AgentFormatter.onTestRunFinished you now return early when issueCount === 0, which removes the previous "All scenarios passed" messaging entirely; if that feedback is still desired, consider keeping a minimal success summary instead of returning silently.
  • The verify.ts script prints a custom failure line after runSilentCommandSequence has already replayed the failing step's stdout/stderr; consider including the step exit status in that replay or adjusting the extra message to avoid redundant-but-slightly-different failure context.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `AgentFormatter.onTestRunFinished` you now return early when `issueCount === 0`, which removes the previous "All scenarios passed" messaging entirely; if that feedback is still desired, consider keeping a minimal success summary instead of returning silently.
- The `verify.ts` script prints a custom failure line after `runSilentCommandSequence` has already replayed the failing step's stdout/stderr; consider including the step exit status in that replay or adjusting the extra message to avoid redundant-but-slightly-different failure context.

## Individual Comments

### Comment 1
<location path="build-pipeline/scripts/verify.ts" line_range="29-38" />
<code_context>
+	.addStep(sonarPullRequestAnalysis())
+	.addStep(sonarQualityGate());
+
+const isRunningAsCommand = import.meta.main;
+
+function runVerifyCommand(): void {
+	const result = cellixVerify.run();
+	if (result.status !== 0) {
+		process.stderr.write(`\nverify failed at step "${result.step.name}" (exit ${result.status})\n`);
+	} else {
+		process.stdout.write('verify passed\n');
+	}
+	process.exitCode = result.status;
+}
+
+if (isRunningAsCommand) {
+	runVerifyCommand();
+}
</code_context>
<issue_to_address>
**issue (bug_risk):** Using `import.meta.main` will never trigger in Node.js and prevents the script from running when executed directly.

In Node.js, `import.meta.main` is always `undefined`, so `runVerifyCommand()` never runs, even when this file is invoked as the entry script (e.g. via `pnpm run verify`). Use a Node-compatible check instead, such as comparing `import.meta.url` to `pathToFileURL(process.argv[1]).href`, or invoke `runVerifyCommand()` from a dedicated bootstrap file that always calls it.
</issue_to_address>

### Comment 2
<location path="package.json" line_range="52" />
<code_context>
 		"sonar:pr-windows": "for /f %i in ('node build-pipeline/scripts/get-pr-number.cjs') do set PR_NUMBER=%i && sonar-scanner -Dsonar.pullrequest.key=%PR_NUMBER% -Dsonar.pullrequest.branch=%BRANCH_NAME% -Dsonar.pullrequest.base=main",
 		"check-sonar": "node build-pipeline/scripts/check-sonar-quality-gate.cjs",
-		"verify": "pnpm run format:check && pnpm run test:arch && pnpm run test:coverage:merge && pnpm run test:e2e && pnpm run knip && pnpm run audit && pnpm run snyk && pnpm run sonar:pr && pnpm run check-sonar",
+		"verify": "node build-pipeline/scripts/verify.ts",
 		"knip": "knip",
 		"snyk": "pnpm run snyk:test && pnpm run snyk:code",
</code_context>
<issue_to_address>
**issue (bug_risk):** Running a `.ts` file directly with `node` will fail without a custom loader or precompiled JS.

This script now calls `node build-pipeline/scripts/verify.ts`, but Node can’t run TypeScript directly. Unless there’s an existing loader configured for this command (e.g. `tsx`, `ts-node`, or a custom `--loader`), it will fail at runtime. Consider either targeting the compiled `.js` file or running it via the configured TS runner instead of plain `node`.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread build-pipeline/scripts/verify.ts Outdated
Comment thread package.json Outdated
@jasonmorais

Copy link
Copy Markdown
Contributor Author

image For reference

@jasonmorais

Copy link
Copy Markdown
Contributor Author

@sourcery-ai review

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="build-pipeline/scripts/verify.ts" line_range="29-38" />
<code_context>
+	.addStep(sonarPullRequestAnalysis())
+	.addStep(sonarQualityGate());
+
+const isRunningAsCommand = import.meta.main;
+
+function runVerifyCommand(): void {
+	const result = cellixVerify.run();
+	if (result.status !== 0) {
+		process.stderr.write(`\nverify failed at step "${result.step.name}" (exit ${result.status})\n`);
+	} else {
+		process.stdout.write('verify passed\n');
+	}
+	process.exitCode = result.status;
+}
+
+if (isRunningAsCommand) {
+	runVerifyCommand();
+}
</code_context>
<issue_to_address>
**issue (bug_risk):** Using `import.meta.main` is likely non-portable and may prevent the verify script from running when invoked directly.

In Node’s current ESM implementation `import.meta.main` is not available, so `isRunningAsCommand` will be `undefined` and `runVerifyCommand()` will never run, even when you call `node build-pipeline/scripts/verify.ts` directly. To ensure the script actually executes as a CLI, switch to the common pattern of comparing `import.meta.url` with `pathToFileURL(process.argv[1]).href` (or another supported entry-point check).
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread build-pipeline/scripts/verify.ts Outdated
@jasonmorais

Copy link
Copy Markdown
Contributor Author

@sourcery-ai review

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • The new verify.ts script prints verify passed on success, which slightly conflicts with the goal of keeping successful verification runs silent; consider making this success message optional (e.g., behind a flag) or removing it to keep the workflow fully quiet when healthy.
  • The runSilentCommand helper hardcodes a 64MB maxBuffer; if you expect some tools to produce larger output, consider exposing maxBuffer (or a similar option) on SilentCommandOptions so callers can tune this per command.
  • The construction of the cellixVerify sequence in verify.ts is fairly specific and could be reused elsewhere; consider extracting this builder into a small shared helper in @cellix/local-dev (or a nearby module) so local/CI verification flows can share the same sequence definition without duplication.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The new `verify.ts` script prints `verify passed` on success, which slightly conflicts with the goal of keeping successful verification runs silent; consider making this success message optional (e.g., behind a flag) or removing it to keep the workflow fully quiet when healthy.
- The `runSilentCommand` helper hardcodes a 64MB `maxBuffer`; if you expect some tools to produce larger output, consider exposing `maxBuffer` (or a similar option) on `SilentCommandOptions` so callers can tune this per command.
- The construction of the `cellixVerify` sequence in `verify.ts` is fairly specific and could be reused elsewhere; consider extracting this builder into a small shared helper in `@cellix/local-dev` (or a nearby module) so local/CI verification flows can share the same sequence definition without duplication.

## Individual Comments

### Comment 1
<location path="packages/cellix/local-dev/src/silent-runners/index.ts" line_range="177-186" />
<code_context>
+ * @param options - Sequence steps and optional process context.
+ * @returns The final successful step result or the first failing step result.
+ */
+export function runSilentCommandSequence(options: SilentCommandSequenceOptions): SilentCommandSequenceResult {
+	const { cwd, env, spawn, steps, streams } = options;
+	if (steps.length === 0) {
+		throw new Error('runSilentCommandSequence requires at least one step');
+	}
+
+	let lastResult: SilentCommandSequenceResult | undefined;
+
+	for (const step of steps) {
+		const context: CommandSequenceContext = { cwd, env, spawn, streams };
+		const result = step.output === 'inherit' ? runInheritedCommand(buildCommandOptions(step, context)) : runSilentCommand(buildCommandOptions(step, context));
+		lastResult = {
+			...result,
+			step,
+		};
+
+		if (result.status !== 0) {
+			return lastResult;
+		}
+	}
+
+	if (!lastResult) {
+		throw new Error('runSilentCommandSequence requires at least one step');
+	}
</code_context>
<issue_to_address>
**nitpick:** The `runSilentCommandSequence` empty-steps guard is duplicated and could be simplified.

The function throws `runSilentCommandSequence requires at least one step` both on the initial `steps.length === 0` check and again when `!lastResult` after the loop. Because of the initial check, the post-loop guard is unreachable and can be removed, or you can drop the initial check and rely solely on the `lastResult` check to cover the empty-steps case.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread packages/cellix/local-dev/src/silent-runners/index.ts Outdated
@jasonmorais jasonmorais changed the title initial commit for precommit silent verify script Jun 23, 2026
@jasonmorais

Copy link
Copy Markdown
Contributor Author

@sourcery-ai review

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've left some high level feedback:

  • The new verify runner currently writes verify passed on success, which slightly conflicts with the "silent on success" contract of the silent runners; consider omitting this message so local runs stay fully quiet when healthy.
  • In replayFailureOutput, the stderr replay concatenates the failure banner and the tool stderr without ensuring a trailing newline from the tool output; consider always terminating the combined message with a newline to avoid malformed prompts when the tool's stderr is not newline-terminated.
  • The Snyk helper snykIacScan takes targets as the first argument whereas the other Snyk helpers accept only an options object; consider making its signature consistent (e.g., snykIacScan({ targets, args, name })) to keep the public API uniform.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The new verify runner currently writes `verify passed` on success, which slightly conflicts with the "silent on success" contract of the silent runners; consider omitting this message so local runs stay fully quiet when healthy.
- In `replayFailureOutput`, the stderr replay concatenates the failure banner and the tool stderr without ensuring a trailing newline from the tool output; consider always terminating the combined message with a newline to avoid malformed prompts when the tool's stderr is not newline-terminated.
- The Snyk helper `snykIacScan` takes `targets` as the first argument whereas the other Snyk helpers accept only an options object; consider making its signature consistent (e.g., `snykIacScan({ targets, args, name })`) to keep the public API uniform.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

# Conflicts:
#	package.json
#	packages/cellix/serenity-framework/src/formatters/agent-formatter.ts
@jasonmorais

Copy link
Copy Markdown
Contributor Author

@sourcery-ai review

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

Fixed security issues:

  • @opentelemetry/exporter-prometheus (link)

  • @vitest/browser (link)

  • form-data (link)

  • protobufjs (link)

  • vite (link)

  • ws (link)

  • Some of the step-definition and helper changes collapse previously multi-line logical conditions and actor.attemptsTo calls into single very long lines, which hurts readability; consider keeping these broken across lines to preserve clarity and easier diffs.

  • For runSilentCommandSequence failures you currently only show the raw command line in replayFailureOutput; consider also including the CommandSequenceStep.name so failures are easier to correlate with the higher-level verify steps (e.g. snyk:code, audit:prod).

Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Some of the step-definition and helper changes collapse previously multi-line logical conditions and `actor.attemptsTo` calls into single very long lines, which hurts readability; consider keeping these broken across lines to preserve clarity and easier diffs.
- For `runSilentCommandSequence` failures you currently only show the raw command line in `replayFailureOutput`; consider also including the `CommandSequenceStep.name` so failures are easier to correlate with the higher-level verify steps (e.g. `snyk:code`, `audit:prod`).

## Individual Comments

### Comment 1
<location path="packages/cellix/local-dev/src/silent-runners/index.ts" line_range="283-293" />
<code_context>
+		...(step.args ? { args: step.args } : {}),
+		...(options.cwd ? { cwd: options.cwd } : {}),
+		...(options.env ? { env: options.env } : {}),
+		...(step.maxBuffer !== undefined ? { maxBuffer: step.maxBuffer } : options.maxBuffer !== undefined ? { maxBuffer: options.maxBuffer } : {}),
+		...(options.spawn ? { spawn: options.spawn } : {}),
+		...(options.streams ? { streams: options.streams } : {}),
</code_context>
<issue_to_address>
**suggestion:** Nested ternary for resolving maxBuffer hurts readability and is easy to misinterpret.

Consider replacing this nested ternary with clearer logic, e.g. compute `const maxBuffer = step.maxBuffer ?? options.maxBuffer;` and then conditionally spread `{ maxBuffer }`, or use straightforward `if` branches. This makes the precedence explicit and reduces the chance of mistakes if more options are added later.

```suggestion
function buildCommandOptions(step: CommandSequenceStep, options: CommandSequenceContext): SilentCommandOptions {
	const maxBuffer = step.maxBuffer ?? options.maxBuffer;

	return {
		...(step.args ? { args: step.args } : {}),
		...(options.cwd ? { cwd: options.cwd } : {}),
		...(options.env ? { env: options.env } : {}),
		...(maxBuffer !== undefined ? { maxBuffer } : {}),
		...(options.spawn ? { spawn: options.spawn } : {}),
		...(options.streams ? { streams: options.streams } : {}),
		command: step.command,
	};
}
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread packages/cellix/local-dev/src/silent-runners/index.ts
# Conflicts:
#	pnpm-lock.yaml
#	pnpm-workspace.yaml
# Conflicts:
#	packages/cellix/local-dev/README.md
#	packages/cellix/local-dev/manifest.md
#	packages/cellix/local-dev/package.json
#	packages/cellix/local-dev/src/index.test.ts
#	packages/cellix/local-dev/src/index.ts
#	packages/cellix/local-dev/tsconfig.vitest.json
#	packages/cellix/local-dev/vitest.config.ts
#	pnpm-lock.yaml
#	pnpm-workspace.yaml
noce-nick
noce-nick previously approved these changes Jul 10, 2026
@jasonmorais

Copy link
Copy Markdown
Contributor Author

@sourcery-ai review

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've reviewed your changes and they look great!

Fixed security issues:

  • @opentelemetry/exporter-prometheus (link)
  • @vitest/browser (link)
  • form-data (link)
  • protobufjs (link)
  • vite (link)
  • ws (link)

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@jasonmorais

Copy link
Copy Markdown
Contributor Author

@sourcery-ai review

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue

Fixed security issues:

  • @opentelemetry/exporter-prometheus (link)
  • @vitest/browser (link)
  • form-data (link)
  • protobufjs (link)
  • vite (link)
  • ws (link)
Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="packages/cellix/local-dev/src/silent-runners/index.ts" line_range="297-301" />
<code_context>
+	};
+}
+
+function toSilentCommandResult(result: SpawnSyncReturns<string>): SilentCommandResult {
+	return {
+		...(result.error ? { error: result.error } : {}),
+		signal: result.signal,
+		status: result.status ?? 1,
+		stderr: result.stderr ?? '',
+		stdout: result.stdout ?? '',
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Clarify handling of signal-terminated processes so status and messaging stay consistent.

For signal-terminated children, `result.status` is `null` and `result.signal` is set. Forcing `status` to `1` here will cause `replayFailureOutput` to print `exit 1`, losing the signal context and giving a misleading reason for failure. Consider either mapping the signal to a conventional status while still surfacing the signal in the failure output, or leaving `status` null and special‑casing the message (e.g. `Command failed (signal SIGTERM)`).

Suggested implementation:

```typescript
function toSilentCommandResult(result: SpawnSyncReturns<string>): SilentCommandResult {
	const status =
		result.status !== null && result.status !== undefined
			? result.status
			: result.signal
				? null
				: 1;

	return {
		...(result.error ? { error: result.error } : {}),
		signal: result.signal,
		status,
		stderr: result.stderr ?? '',
		stdout: result.stdout ?? '',
	};
}

```

To fully implement the suggestion:
1. Update the `SilentCommandResult` type (where it is defined) so that `status` can be `number | null` instead of just `number`, if it is currently restricted to `number`.
2. In the code that prints or logs failures (e.g. the `replayFailureOutput` helper referenced in your comment), special‑case the messaging:
   - If `result.signal` is set and `result.status` is `null`, render something like: `Command failed (signal SIGTERM)` instead of `exit 1`.
   - Otherwise, keep the existing `exit <status>` message for non‑signal failures.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread packages/cellix/local-dev/src/silent-runners/index.ts Outdated
@jasonmorais

Copy link
Copy Markdown
Contributor Author

@sourcery-ai review

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

Fixed security issues:

  • @opentelemetry/exporter-prometheus (link)

  • @vitest/browser (link)

  • form-data (link)

  • protobufjs (link)

  • vite (link)

  • ws (link)

  • For SilentRunnerStreams, consider typing the stdout/stderr fields as a more general NodeJS.WritableStream (or a custom interface) rather than Pick<NodeJS.WriteStream, 'write'> so non-TTY writable streams can be used without type assertions.

  • runSilentCommandSequence skips replayFailureOutput for steps with output: 'inherit', so failures on those steps don’t get a standardized header or error replay; consider still emitting the failure header (and any spawn error) for inherited-output steps to keep failure reporting consistent across the sequence.

Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- For `SilentRunnerStreams`, consider typing the `stdout`/`stderr` fields as a more general `NodeJS.WritableStream` (or a custom interface) rather than `Pick<NodeJS.WriteStream, 'write'>` so non-TTY writable streams can be used without type assertions.
- `runSilentCommandSequence` skips `replayFailureOutput` for steps with `output: 'inherit'`, so failures on those steps don’t get a standardized header or error replay; consider still emitting the failure header (and any spawn error) for inherited-output steps to keep failure reporting consistent across the sequence.

## Individual Comments

### Comment 1
<location path="packages/cellix/local-dev/src/silent-runners/index.ts" line_range="329-338" />
<code_context>
+	});
+}
+
+function replayFailureOutput(result: SilentCommandResult, streams: SilentRunnerStreams, command: string, args: string[]): void {
+	const commandLine = [command, ...args].join(' ');
+	const reason = result.signal ? `signal ${result.signal}` : `exit ${result.status}`;
+	streams.stderr?.write(`\nCommand failed (${reason}): ${commandLine}\n\n`);
+	if (result.stdout) {
+		streams.stdout?.write(result.stdout);
+	}
+	if (result.stderr) {
+		streams.stderr?.write(result.stderr);
+	}
+	if (result.error) {
+		streams.stderr?.write(`${result.error.message}\n`);
+	}
+}
</code_context>
<issue_to_address>
**🚨 suggestion (security):** Consider avoiding logging the full command line on failure to prevent leaking potentially sensitive arguments.

The failure banner currently logs the full reconstructed command line:

```ts
const commandLine = [command, ...args].join(' ');
streams.stderr?.write(`\nCommand failed (${reason}): ${commandLine}\n\n`);
```
If any step passes tokens or credentials via `args`, they will be exposed in logs. Consider letting callers provide a safe, redacted display string (e.g. `displayName`/`redactedCommand`) or a flag to omit arguments and only show the executable name, so CI consumers can control how much detail is logged.

Suggested implementation:

```typescript
function replayFailureOutput(
	result: SilentCommandResult,
	streams: SilentRunnerStreams,
	command: string,
	args: string[],
	displayCommand?: string,
	logArgumentsInFailureBanner: boolean = false,
): void {
	const reason = result.signal ? `signal ${result.signal}` : `exit ${result.status}`;

	// Prefer an explicit, redacted display string when provided.
	// Otherwise, optionally include arguments in the banner based on the flag,
	// defaulting to only the executable name to avoid leaking sensitive data.
	const bannerCommand =
		displayCommand ??
		(logArgumentsInFailureBanner && args.length > 0
			? [command, ...args].join(' ')
			: command);

	streams.stderr?.write(`\nCommand failed (${reason}): ${bannerCommand}\n\n`);

	if (result.stdout) {
		streams.stdout?.write(result.stdout);
	}
	if (result.stderr) {
		streams.stderr?.write(result.stderr);
	}
	if (result.error) {
		streams.stderr?.write(`${result.error.message}\n`);
	}
}

```

```typescript
export interface SilentCommandOptions {
	/** Executable name. */
	command: string;
	/** Command-line arguments passed without shell interpolation. */
	args?: string[];
	/**
	 * Human-readable, redacted command label to use in logs instead of the raw command/args.
	 * If omitted, logging falls back to either the full command line or just the executable name,
	 * depending on `logArgumentsInFailureBanner`.
	 */
	displayCommand?: string;
	/**
	 * Controls whether arguments are included in the failure banner when `displayCommand` is not provided.
	 * Defaults to `false` to avoid leaking sensitive data via arguments.
	 */
	logArgumentsInFailureBanner?: boolean;
	/** Working directory for the command. Defaults to the current process cwd. */

```

To fully implement this behavior, you’ll also need to:
1. Update all call sites of `replayFailureOutput` to pass `options.displayCommand` and `options.logArgumentsInFailureBanner` (or leave them undefined to keep the new default of not logging arguments). For example:
   - Previously: `replayFailureOutput(result, streams, options.command, options.args ?? []);`
   - Now: `replayFailureOutput(result, streams, options.command, options.args ?? [], options.displayCommand, options.logArgumentsInFailureBanner ?? false);`
2. Ensure that code constructing `SilentCommandOptions` in this module (and any consumers) sets `displayCommand` to a redacted/summary form when arguments may contain secrets, or relies on the default behavior (only executable name) by leaving both `displayCommand` and `logArgumentsInFailureBanner` unset.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread packages/cellix/local-dev/src/silent-runners/index.ts
@jasonmorais

Copy link
Copy Markdown
Contributor Author

@sourcery-ai review

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've reviewed your changes and they look great!

Fixed security issues:

  • @opentelemetry/exporter-prometheus (link)
  • @vitest/browser (link)
  • form-data (link)
  • protobufjs (link)
  • vite (link)
  • ws (link)

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants